package com.xinyu.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xinyu.config.FileStorageProperties;
import com.xinyu.domain.Daily;
import com.xinyu.domain.DailyAttachment;
import com.xinyu.domain.User;
import com.xinyu.mapper.DailyMapper;
import com.xinyu.service.IDailyAttachmentService;
import com.xinyu.service.IDailyService;
import com.xinyu.vo.BaseResult;
import com.xinyu.vo.DailyRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author: XinYu
 * @since 2025-02-17
 */
@Service
public class DailyServiceImpl extends ServiceImpl<DailyMapper, Daily> implements IDailyService {

    @Autowired
    private DailyMapper dailyMapper;

    @Autowired
    private FileStorageProperties fileStorageProperties;

    @Autowired
    private IDailyAttachmentService dailyAttachmentService;

    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Override
    public BaseResult queryByCondition(DailyRequest dailyRequest) {
        List<Daily> list = dailyMapper.queryByCondition(dailyRequest);
        enrichDailyList(list);
        Integer dailyCounts = dailyMapper.dailyCounts(dailyRequest);
        return BaseResult.ok(list, dailyCounts);
    }

    @Override
    public Daily findById(Integer id) {
        Daily daily = dailyMapper.queryById(id);
        enrichDailyList(Collections.singletonList(daily));
        return daily;
    }

    @Override
    public int syncStateWithTask() {
        return dailyMapper.syncStateWithTask();
    }

    @Override
    public void saveDailyWithAttachments(Daily daily, Integer operatorId) {
        if (daily.getAttachments() == null) {
            daily.setAttachments(Collections.emptyList());
        }
        normalizeIncomingAttachments(daily, operatorId);
        save(daily);
        persistAttachments(daily, operatorId, true);
    }

    @Override
    public void updateDailyWithAttachments(Daily daily, Integer operatorId) {
        boolean attachmentsProvided = daily.getAttachments() != null;
        if (attachmentsProvided) {
            normalizeIncomingAttachments(daily, operatorId);
        }
        updateById(daily);
        if (attachmentsProvided) {
            persistAttachments(daily, operatorId, false);
        }
    }

    private void enrichDailyList(List<Daily> list) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        attachUserAvatar(list);
        attachFiles(list);
    }

    private void attachUserAvatar(List<Daily> list) {
        for (Daily daily : list) {
            User user = daily.getUser();
            if (user != null && StringUtils.isNotBlank(user.getHead()) &&
                    !user.getHead().contains(fileStorageProperties.getBaseUrl())) {
                user.setHead(fileStorageProperties.getBaseUrl() + user.getHead());
            }
        }
    }

    private void attachFiles(List<Daily> list) {
        List<Integer> ids = list.stream()
                .map(Daily::getId)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(ids)) {
            return;
        }
        List<DailyAttachment> attachmentList = dailyAttachmentService.list(
                new LambdaQueryWrapper<DailyAttachment>()
                        .in(DailyAttachment::getDailyId, ids)
                        .orderByAsc(DailyAttachment::getId)
        );
        Map<Integer, List<DailyAttachment>> grouped = attachmentList.stream()
                .map(this::buildAttachmentForResponse)
                .collect(Collectors.groupingBy(DailyAttachment::getDailyId));
        for (Daily daily : list) {
            List<DailyAttachment> attachments = grouped.get(daily.getId());
            if (CollectionUtils.isEmpty(attachments) && StringUtils.isNotBlank(daily.getAttachment())) {
                DailyAttachment fallback = new DailyAttachment();
                fallback.setDailyId(daily.getId());
                fallback.setFileUrl(prefixBaseUrl(daily.getAttachment()));
                fallback.setFileName(daily.getAttachmentName());
                attachments = new ArrayList<>();
                attachments.add(fallback);
            }
            daily.setAttachments(CollectionUtils.isEmpty(attachments) ? Collections.emptyList() : attachments);
            if (StringUtils.isNotBlank(daily.getAttachment()) &&
                    !daily.getAttachment().contains(fileStorageProperties.getBaseUrl())) {
                daily.setAttachment(prefixBaseUrl(daily.getAttachment()));
            }
        }
    }

    private DailyAttachment buildAttachmentForResponse(DailyAttachment attachment) {
        if (StringUtils.isNotBlank(attachment.getFileUrl()) &&
                !attachment.getFileUrl().startsWith(fileStorageProperties.getBaseUrl())) {
            attachment.setFileUrl(prefixBaseUrl(attachment.getFileUrl()));
        }
        return attachment;
    }

    private void normalizeIncomingAttachments(Daily daily, Integer operatorId) {
        List<DailyAttachment> attachments = daily.getAttachments();
        if (CollectionUtils.isEmpty(attachments)) {
            daily.setAttachments(Collections.emptyList());
            daily.setAttachment(null);
            daily.setAttachmentName(null);
            return;
        }
        List<DailyAttachment> sanitized = attachments.stream()
                .filter(att -> StringUtils.isNotBlank(att.getFileUrl()))
                .map(att -> {
                    DailyAttachment copy = new DailyAttachment();
                    copy.setId(att.getId());
                    copy.setDailyId(daily.getId());
                    copy.setFileName(att.getFileName());
                    copy.setFileSize(att.getFileSize());
                    copy.setMimeType(att.getMimeType());
                    copy.setFileUrl(stripBaseUrl(att.getFileUrl()));
                    copy.setCreatedBy(att.getCreatedBy() != null ? att.getCreatedBy() : operatorId);
                    copy.setCreatedTime(att.getCreatedTime());
                    return copy;
                })
                .collect(Collectors.toList());
        daily.setAttachments(sanitized);
        if (!CollectionUtils.isEmpty(sanitized)) {
            DailyAttachment first = sanitized.get(0);
            daily.setAttachment(first.getFileUrl());
            daily.setAttachmentName(first.getFileName());
        } else {
            daily.setAttachment(null);
            daily.setAttachmentName(null);
        }
    }

    private void persistAttachments(Daily daily, Integer operatorId, boolean isCreate) {
        List<DailyAttachment> attachments = daily.getAttachments();
        Integer dailyId = daily.getId();
        if (dailyId == null) {
            return;
        }
        if (CollectionUtils.isEmpty(attachments)) {
            if (!isCreate) {
                dailyAttachmentService.remove(new LambdaQueryWrapper<DailyAttachment>()
                        .eq(DailyAttachment::getDailyId, dailyId));
            }
            return;
        }
        if (!isCreate) {
            Set<Integer> keepIds = attachments.stream()
                    .map(DailyAttachment::getId)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toSet());
            List<Integer> needDelete = dailyAttachmentService.list(new LambdaQueryWrapper<DailyAttachment>()
                            .eq(DailyAttachment::getDailyId, dailyId))
                    .stream()
                    .map(DailyAttachment::getId)
                    .filter(id -> id != null && (CollectionUtils.isEmpty(keepIds) || !keepIds.contains(id)))
                    .collect(Collectors.toList());
            if (!needDelete.isEmpty()) {
                dailyAttachmentService.removeByIds(needDelete);
            }
        }
        List<DailyAttachment> toPersist = attachments.stream()
                .filter(att -> att.getId() == null)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(toPersist)) {
            return;
        }
        String now = LocalDateTime.now().format(DATE_TIME_FORMATTER);
        toPersist.forEach(att -> {
            if (isCreate) {
                att.setId(null);
            }
            att.setDailyId(dailyId);
            att.setCreatedBy(att.getCreatedBy() != null ? att.getCreatedBy() : operatorId);
            att.setCreatedTime(now);
        });
        dailyAttachmentService.saveBatch(toPersist);
    }

    private String prefixBaseUrl(String relativePath) {
        if (StringUtils.isBlank(relativePath)) {
            return relativePath;
        }
        if (relativePath.startsWith(fileStorageProperties.getBaseUrl())) {
            return relativePath;
        }
        return fileStorageProperties.getBaseUrl() + relativePath;
    }

    private String stripBaseUrl(String path) {
        if (StringUtils.isBlank(path)) {
            return path;
        }
        if (path.startsWith(fileStorageProperties.getBaseUrl())) {
            return path.substring(fileStorageProperties.getBaseUrl().length());
        }
        return path;
    }
}
